namespace Gsl
{
    using System;
    using System.Runtime.InteropServices;

    public class Integration
    {
        private Function function;
        private gsl_function_struct gfs;
        private object parameters;
        private double epsrel;
        private double epsabs;
        private double result;
        private double error;
        private int num_intervals;
        private IntPtr workspace;
        
        [DllImport("libgsl")]
        private static extern IntPtr gsl_integration_workspace_alloc(int size);
        
        [DllImport("libgsl")]
        private static extern void gsl_integration_workspace_free(IntPtr workspace);
        
        private double gsl_function_magic(double x, IntPtr p)
        {
            return function(x, parameters);
        }
        
        public Integration(int num_intervals)
        {
            this.num_intervals = num_intervals;
            workspace = gsl_integration_workspace_alloc(num_intervals);
            
            function = null;
            parameters = null;
            epsrel = 1e-7;
            epsabs = 0.0;
            
            gfs.Function = gsl_function_magic;
            gfs.Params = IntPtr.Zero;
        }
        
        ~Integration()
        {
            gsl_integration_workspace_free(workspace);
        }
        
        public Function Function
        {
            set { function = value; }
        }
        
        public object Parameters
        {
            set { parameters = value; }
        }
        
        public double RelativeErrorLimit
        {
            get { return epsrel; }
            set { epsrel = value; }
        }
        
        public double AbsoluteErrorLimit
        {
            get { return epsabs; }
            set { epsabs = value; }
        }
        
        public double Result
        {
            get { return result; }
        }
        
        public double Error
        {
            get { return error; }
        }
        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qng(ref gsl_function_struct f, double a, double b, double epsabs, double epsrel, ref double result, ref double error);
        
        public void ExecuteQNG(double lower_limit, double upper_limit)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qng(ref gfs,
                                lower_limit,
                                upper_limit,
                                epsabs,
                                epsrel,
                                ref result,
                                ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QNG: {0}", ret), ret);
        }
        
        public enum Key { GSL_INTEG_GAUSS15 = 1,      /* 15 point Gauss-Kronrod rule */
                          GSL_INTEG_GAUSS21 = 2,      /* 21 point Gauss-Kronrod rule */
                          GSL_INTEG_GAUSS31 = 3,      /* 31 point Gauss-Kronrod rule */
                          GSL_INTEG_GAUSS41 = 4,      /* 41 point Gauss-Kronrod rule */
                          GSL_INTEG_GAUSS51 = 5,      /* 51 point Gauss-Kronrod rule */
                          GSL_INTEG_GAUSS61 = 6       /* 61 point Gauss-Kronrod rule */
                        };
                        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qag(ref gsl_function_struct f, double a, double b, double epsabs, double epsrel, int limit, Key key, IntPtr workspace, ref double result, ref double error);        
        
        public void ExecuteQAG(double lower_limit, double upper_limit, Key key)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qag(ref gfs,
                                           lower_limit,
                                           upper_limit,
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           key,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAG: {0}", ret), ret);
        }
                        
        public void ExecuteQAG(double lower_limit, double upper_limit)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qag(ref gfs,
                                           lower_limit,
                                           upper_limit,
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           Key.GSL_INTEG_GAUSS15,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAG: {0}", ret), ret);
        }        	
        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qags(ref gsl_function_struct f, double a, double b, double epsabs, double epsrel, int limit, IntPtr workspace, ref double result, ref double error);

        public void ExecuteQAGS(double lower_limit, double upper_limit)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qags(ref gfs,
                                           lower_limit,
                                           upper_limit,
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAGS: {0}", ret), ret);
        }
        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qagi(ref gsl_function_struct f, double epsabs, double epsrel, int limit, IntPtr workspace, ref double result, ref double error);
        
        /// <summary>
        /// Integrate the function using ...
        /// </summary>   
        public void ExecuteQAGI()
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qagi(ref gfs,
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAGI: {0}", ret), ret);
        }
        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qagiu(ref gsl_function_struct f, double a, double epsabs, double epsrel, int limit, IntPtr workspace, ref double result, ref double error);
        
        /// <summary>
        /// Integrate the function using ...
        /// </summary>
        public void ExecuteQAGIU(double lower_limit)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qagiu(ref gfs,
                                           lower_limit,
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAGIU: {0}", ret), ret);
        }
        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qagil(ref gsl_function_struct f, double b, double epsabs, double epsrel, int limit, IntPtr workspace, ref double result, ref double error);
        
        /// <summary>
        /// Integrate the function using ...
        /// </summary>
        public void ExecuteQAGIL(double upper_limit)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qagil(ref gfs,
                                           upper_limit,
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAGIL: {0}", ret), ret);
        }
        
        [DllImport("libgsl")]
        private static extern IntPtr gsl_integration_qaws_table_alloc(double alpha, double beta, double mu, double nu);
        [DllImport("libgsl")]
        private static extern IntPtr gsl_integration_qaws_table_free(IntPtr table);
        [DllImport("libgsl")]
        private static extern IntPtr gsl_integration_qaws_table_set(IntPtr table, double alpha, double beta, double mu, double nu);
        
        public class Table
        {
        	internal IntPtr table;
        	
        	public Table(double alpha, double beta, double mu, double nu)
        	{
        		table = gsl_integration_qaws_table_alloc(alpha, beta, mu, nu);
        	}
        	
        	~Table()
        	{
        		gsl_integration_qaws_table_free(table);
        	}
        	
        	public void Set(double alpha, double beta, double mu, double nu)
        	{
        		gsl_integration_qaws_table_set(table, alpha, beta, mu, nu);
        	}
        }
        
        [DllImport("libgsl")]
        private static extern int gsl_integration_qaws(ref gsl_function_struct f, double a, double b, IntPtr table, double epsabs, double epsrel, int limit, IntPtr workspace, ref double result, ref double abserr);
        
        public void ExecuteQAWS(double a, double b, Table table)
        {
            Gsl.Error.Code ret = (Gsl.Error.Code)gsl_integration_qaws(ref gfs,
                                           a, b,
                                           table.table, 
                                           epsabs,
                                           epsrel,
                                           num_intervals,
                                           workspace,
                                           ref result,
                                           ref error);
                                           
            if (ret != Gsl.Error.Code.GSL_SUCCESS)
                throw new GslException(String.Format("GSL error in executing QAGIL: {0}", ret), ret);
        }
    }
}
